#include "datacenter.h"

#include <QDir>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QStandardPaths>

#include "user.qpb.h"
#include "friend.qpb.h"
#include "message_storage.qpb.h"

namespace model {

DataCenter* DataCenter::instance = nullptr;

DataCenter::DataCenter() : netClient(this)
{
    recentMessages = new QHash<QString, QList<Message>>();
    memberList = new QHash<QString, QList<UserInfo>>();
    unreadMessageCount = new QHash<QString, int>();
    chatSessionIdOfListIdx = new QHash<QString, int>();
    userIdOfListIdx = new QHash<QString, int>();

    // 加载历史数据
    loadDataFile();
}

DataCenter::~DataCenter() {
    delete selfInfo;
    delete friendList;
    delete chatSessionList;
    delete memberList;
    delete recentMessages;
    delete searchHisMessageResult;
    delete unreadMessageCount;
    delete searchUserResult;
}

DataCenter* DataCenter::getInstance()
{
    if(!instance) {
        instance = new DataCenter();
    }
    return instance;
}

QString DataCenter::getDefaultFileName()
{
    return "/ChatClient.json";
}

void DataCenter::initDataFile()
{
    QString basePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QString filePath = basePath + getDefaultFileName();
    LOG(LL_DEBUG, "filePath: {}", filePath);

    QDir dir;
    if(!dir.exists(basePath)) {
        dir.mkpath(basePath);
    }

    QFile file(filePath);
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        LOG(LL_ERROR, "打开 ChatClient.json 失败, {}", file.errorString());
        return;
    }
    QString data = "{\n\n}";
    file.write(data.toUtf8());
    file.close();
}

void DataCenter::saveDataFile()
{
    QString filePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + getDefaultFileName();
    QFile file(filePath);
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        LOG(LL_ERROR, "打开 ChatClient.json 失败, {}", file.errorString());
        return;
    }

    QJsonObject root;
    root["loginSessionId"] = loginSessionId;

    QJsonObject unread;
    for(auto it = unreadMessageCount->begin(); it != unreadMessageCount->end(); ++it) {
        unread[it.key()] = it.value();
    }
    root["unread"] = unread;

    QJsonDocument jsonDoc(root);
    QString data = jsonDoc.toJson();
    file.write(data.toUtf8());
    file.close();
}

void DataCenter::loadDataFile()
{
    QString filePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + getDefaultFileName();

    QFileInfo fileInfo(filePath);
    if(!fileInfo.exists()) {
        initDataFile();
    }

    QFile file(filePath);
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        LOG(LL_ERROR, "打开 ChatClient.json 失败, {}", file.errorString());
        return;
    }

    QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll());
    if(jsonDoc.isNull()) {
        LOG(LL_ERROR, "解析Json文件失败.");
        return;
    }

    QJsonObject root = jsonDoc.object();
    loginSessionId = root["loginSessionId"].toString();

    unreadMessageCount->clear();
    QJsonObject unread = root["unread"].toObject();
    for (auto it = unread.begin(); it != unread.end(); ++it) {
        unreadMessageCount->emplace(it.key(), it.value().toInt());
    }

    file.close();
}

void DataCenter::claerUnread(const QString& chatSessionId)
{
    (*unreadMessageCount)[chatSessionId] = 0;
    saveDataFile();
}

void DataCenter::addUnread(const QString& chatSessionId)
{
    ++(*unreadMessageCount)[chatSessionId];
    saveDataFile();
}

int DataCenter::getUnread(const QString& chatSessionId)
{
    return unreadMessageCount->value(chatSessionId);
}

QString DataCenter::getLoginSessionId()
{
    return loginSessionId;
}

UserInfo* DataCenter::getSelfInfo()
{
    return selfInfo;
}

void DataCenter::getSelfInfoAsync()
{
    // DataCenter只负责数据处理, 网络通信数据传输由NetClient处理
    netClient.getSelfInfo(loginSessionId);
}

void DataCenter::resetSelfInfo(std::shared_ptr<im_proto::GetUserInfoRsp> resp)
{
    if(!selfInfo) {
        selfInfo = new UserInfo();
    }
    const im_proto::UserInfo& userInfo = resp->userInfo();
    selfInfo->load(userInfo);
}

QList<UserInfo> *DataCenter::getFriendList()
{
    return friendList;
}

void DataCenter::getFriendListAsync()
{
    netClient.getFriendList(loginSessionId);
}

void DataCenter::resetFriendList(std::shared_ptr<im_proto::GetFriendListRsp> resp)
{
    if(!friendList) {
        friendList = new QList<UserInfo>();
    }

    friendList->clear();

    const QList<im_proto::UserInfo>& friendListPB = resp->friendList();
    for(auto& f : friendListPB) {
        UserInfo userInfo;
        userInfo.load(f);
        friendList->emplace_back(userInfo);
    }
}

QList<ChatSessionInfo> *DataCenter::getChatSessionList()
{
    return chatSessionList;
}

void DataCenter::getChatSessionListAsync()
{
    netClient.getChatSessionList(loginSessionId);
}

void DataCenter::resetChatSessionList(std::shared_ptr<im_proto::GetChatSessionListRsp> resp)
{
    if (!chatSessionList) {
        chatSessionList = new QList<ChatSessionInfo>();
    }
    chatSessionList->clear();
    chatSessionIdOfListIdx->clear();
    userIdOfListIdx->clear();
    displayOrder.clear();
    displayOrderMap.clear();

    const QList<im_proto::ChatSessionInfo>& chatSessionListPB = resp->chatSessionInfoList();
    int i = 0;
    for (auto& cs : chatSessionListPB) {
        ChatSessionInfo chatSessionInfo;
        chatSessionInfo.load(cs);

        chatSessionList->emplace_back(chatSessionInfo);
        chatSessionIdOfListIdx->emplace(chatSessionInfo.chatSessionId, i);
        userIdOfListIdx->emplace(chatSessionInfo.userId, i);

        // 显示顺序链表
        displayOrder.push_back(i);
        auto it = std::prev(displayOrder.end());
        displayOrderMap.emplace(chatSessionInfo.chatSessionId, it);

        ++i;
    }
}

QList<UserInfo>* DataCenter::getApplyList()
{
    return applyList;
}

void DataCenter::getApplyListAsync()
{
    netClient.getApplyList(loginSessionId);
}

void DataCenter::resetApplyList(std::shared_ptr<im_proto::GetPendingFriendEventListRsp> resp)
{
    if(!applyList) {
        applyList = new QList<UserInfo>();
    }

    applyList->clear();

    const QList<im_proto::FriendEvent> friendEventListPB = resp->event();
    for(auto& fe : friendEventListPB) {
        UserInfo userInfo;
        userInfo.load(fe.sender());
        applyList->emplace_back(userInfo);
    }
}

QList<Message> *DataCenter::getRecentMessageList(const QString& chatSessionId)
{
    return recentMessages->contains(chatSessionId)
        ? &(*recentMessages)[chatSessionId]
        : nullptr;
}

void DataCenter::getRecentMessageListAsync(const QString& chatSessionId, bool updateMessageShow)
{
    netClient.getRecentMessageList(loginSessionId, chatSessionId);
}

void DataCenter::resetRecentMessageList(const QString& chatSessionId, std::shared_ptr<im_proto::GetRecentMsgRsp> resp)
{
    QList<Message>& messageList = (*recentMessages)[chatSessionId];

    messageList.clear();

    const QList<im_proto::MessageInfo>& recentMessageListPB = resp->msgList();
    for(auto& msg : recentMessageListPB) {
        Message message;
        message.load(msg);
        messageList.emplace_back(message);
    }
}

void DataCenter::sendTextMessageAsync(const QString& content)
{
    netClient.sendMessage(loginSessionId, curChatSessionId, MessageType::TEXT_TYPE, content.toUtf8(), "");
}

void DataCenter::sendImageMessageAsync(const QByteArray &content)
{
    netClient.sendMessage(loginSessionId, curChatSessionId, MessageType::IMAGE_TYPE, content, "");
}

void DataCenter::sendFileMessageAsync(const QString& fileName, const QByteArray &content)
{
    netClient.sendMessage(loginSessionId, curChatSessionId, MessageType::FILE_TYPE, content, fileName);
}

void DataCenter::sendSpeechMessageAsync(const QByteArray& content)
{
    netClient.sendMessage(loginSessionId, curChatSessionId, MessageType::SPEECH_TYPE, content, "");
}

void DataCenter::changeNicknameAsync(const QString& nickname)
{
    netClient.changeNicknameAsync(loginSessionId, nickname);
}

void DataCenter::resetNickname(const QString& nickname)
{
    if(!selfInfo) return;
    selfInfo->nickname = nickname;
}

void DataCenter::changeDescriptionAsync(const QString& description)
{
    netClient.changeDescriptionAsync(loginSessionId, description);
}

void DataCenter::resetDescription(const QString& description)
{
    if(!selfInfo) return;
    selfInfo->description = description;
}

void DataCenter::changePhoneAsync(const QString& phone, const QString& verifyCodeId, const QString& verifyCode)
{
    netClient.changePhone(loginSessionId, phone, verifyCodeId, verifyCode);
}

void DataCenter::resetPhone(const QString& phone)
{
    if(!selfInfo) return;
    selfInfo->phone = phone;
}

QString DataCenter::getVerifyCodeId()
{
    return curVerifyCodeId;
}

void DataCenter::getVerifyCodeAsync(const QString &phone)
{
    netClient.getVerifyCode(phone);
}

void DataCenter::resetVerifyCodeId(const QString& verifyCodeId)
{
    curVerifyCodeId = verifyCodeId;
}

void DataCenter::changeAvatarAsync(const QByteArray& imageBytes)
{
    netClient.changeAvatar(loginSessionId, imageBytes);
}

void DataCenter::resetAvatar(const QByteArray& imageBytes)
{
    if(!selfInfo) return;
    selfInfo->avatar = utils::WidgetUtil::makeIcon(imageBytes);
}

void DataCenter::deleteFriendAsync(const QString& userId)
{
    netClient.deleteFriend(loginSessionId, userId);
}

void DataCenter::removeFriend(const QString& userId)
{
    if(!friendList) return;

    // 移除好友列表
    friendList->removeIf([=](const UserInfo& userInfo){
        return userInfo.userId == userId;
    });

    // 删除对应会话
    auto it = userIdOfListIdx->find(userId);
    if(it != userIdOfListIdx->end()) {
        const auto& chatSessionInfo = chatSessionList->value(it.value());

        // 展示顺序表删除维护
        auto displayIt = displayOrderMap[chatSessionInfo.chatSessionId];
        displayOrder.erase(displayIt);
        displayOrderMap.remove(chatSessionInfo.chatSessionId);

        // 如果删除好友为当前选择会话, 需要清空会话
        if(chatSessionInfo.chatSessionId == this->curChatSessionId) {
            emit this->clearCurrentChatSession();
        }

        // 两张好友hash表维护
        chatSessionIdOfListIdx->remove(chatSessionInfo.chatSessionId);
        userIdOfListIdx->erase(it);

        // 固定数据表 friendList 不动, 下次重启客户端时会重新从服务器构建, 只要保证没有hash表可以查找到删除的好友即可
    }
}

void DataCenter::addFriendApplyAsync(const QString& userId)
{
    netClient.addFriendApply(loginSessionId, userId);
}

void DataCenter::friendApplyProcessAysnc(const QString& userId, bool agree)
{
    netClient.friendApplyProcess(loginSessionId, userId, agree);
}

void DataCenter::acceptFriendHandle(const QString& userId)
{
    friendList->emplace_back(this->removeFromApplyList(userId));
}

void DataCenter::createGroupChatSessionAsync(const QList<QString>& userIdList)
{
    netClient.createGroupChatSession(loginSessionId, userIdList);
}

void DataCenter::addChatSession(const ChatSessionInfo& chatSessionInfo)
{
    if(!chatSessionList) {
        LOG(LL_ERROR, "错误行为, 会话列表没有正确初始化");
        return;
    }

    int idx = chatSessionList->size();
    chatSessionIdOfListIdx->emplace(chatSessionInfo.chatSessionId, idx);
    userIdOfListIdx->emplace(chatSessionInfo.userId, idx);
    chatSessionList->emplace_back(chatSessionInfo);

    displayOrder.emplace_front(idx);
    displayOrderMap.emplace(chatSessionInfo.chatSessionId, displayOrder.begin());
}

QList<UserInfo>* DataCenter::getMemberList(const QString& chatSessionId)
{
    return &(*memberList)[chatSessionId];
}

void DataCenter::getMemberListAsync(const QString& chatSessionId)
{
    netClient.getMemberList(loginSessionId, chatSessionId);
}

void DataCenter::resetSessionMemberList(const QString& chatSessionId, const QList<im_proto::UserInfo>& memberList)
{
    QList<UserInfo>& currentMemberList = (*this->memberList)[chatSessionId];
    currentMemberList.clear();

    for(const auto& member : memberList) {
        UserInfo userInfo;
        userInfo.load(member);
        currentMemberList.emplace_back(userInfo);
    }
}

QList<UserInfo>* DataCenter::getSearchUserResult()
{
    return searchUserResult;
}

void DataCenter::searchUserAsync(const QString& key)
{
    netClient.searchUser(loginSessionId, key);
}

void DataCenter::resetSearchUserResult(const QList<im_proto::UserInfo>& searchUserList)
{
    if(!searchUserResult) {
        searchUserResult = new QList<UserInfo>();
    }
    searchUserResult->clear();

    for(const auto& user : searchUserList) {
        UserInfo userInfo;
        userInfo.load(user);
        searchUserResult->emplace_back(userInfo);
    }
}

QList<Message>* DataCenter::getSearchMessageResult()
{
    return searchHisMessageResult;
}

void DataCenter::searchMessageAsync(const QString& key)
{
    netClient.searchMessage(loginSessionId, curChatSessionId, key);
}

void DataCenter::searchMessageByTimeAsync(const QDateTime& beginTime, const QDateTime& endTime)
{
    netClient.searchMessageByTime(loginSessionId, curChatSessionId, beginTime, endTime);
}

void DataCenter::resetSearchMessageResult(const QList<im_proto::MessageInfo>& searchMessageList)
{
    if(!searchHisMessageResult) {
        searchHisMessageResult = new QList<Message>();
    }
    searchHisMessageResult->clear();

    for(const auto& msg : searchMessageList) {
        Message message;
        message.load(msg);
        searchHisMessageResult->emplace_back(message);
    }
}

void DataCenter::AccountLoginAsync(const QString& username, const QString& password)
{
    netClient.AccountLogin(username, password);
}

void DataCenter::phoneLoginAsync(const QString& phone, const QString& verifyCode)
{
    netClient.phoneLogin(phone, this->curVerifyCodeId, verifyCode);
}

void DataCenter::userRegisterAsync(const QString& username, const QString& password, const QString &phone, const QString& verifyCode)
{
    netClient.userRegister(username, password, phone, this->curVerifyCodeId, verifyCode);
}

void DataCenter::resetLoginSessionId(const QString& loginSessionId)
{
    this->loginSessionId = loginSessionId;

    saveDataFile();
}

void DataCenter::getSingleFileAsync(const QString& fileId)
{
    netClient.getSingleFile(loginSessionId, fileId);
}

void DataCenter::speechConvertTextAsync(const QString& fileId, const QByteArray& content)
{
    netClient.speechConvertText(loginSessionId, fileId, content);
}

std::list<int>* DataCenter::getDisplayOrder()
{
    return &displayOrder;
}

QString DataCenter::getCurrentChatSessionId()
{
    return curChatSessionId;
}

void DataCenter::setCurrentChatSessionId(const QString& chatSessionId)
{
    curChatSessionId = chatSessionId;
}

ChatSessionInfo *DataCenter::getChatSessionInfoBySessionId(const QString& chatSessionId)
{
    return chatSessionIdOfListIdx->contains(chatSessionId)
        ? &((*chatSessionList)[(*chatSessionIdOfListIdx)[chatSessionId]])
        : nullptr;
}

ChatSessionInfo *DataCenter::getChatSessionInfoByUserId(const QString& userId)
{
    return userIdOfListIdx->contains(userId)
        ? &((*chatSessionList)[(*userIdOfListIdx)[userId]])
        : nullptr;
}

int DataCenter::getCurChatSessionInfoIndex()
{
    auto it =  chatSessionIdOfListIdx->find(curChatSessionId);
    return it != chatSessionIdOfListIdx->end() ? it.value() : -1;
}

void DataCenter::chatSessionInfoToTop(ChatSessionInfo* chatSessionInfo)
{
    if (!chatSessionInfo) return;

    if(!displayOrderMap.contains(chatSessionInfo->chatSessionId)) return;

    std::list<int>::iterator it = displayOrderMap[chatSessionInfo->chatSessionId];

    if (it == displayOrder.begin()) return;

    // 将 it 位置移动到链表头部
    displayOrder.splice(displayOrder.begin(), displayOrder, it);
}

void DataCenter::addMessage(const Message& message)
{
    (*recentMessages)[message.charSessionId].emplace_back(message);
}

UserInfo* DataCenter::getFriendByUserId(const QString& userId)
{
    if(!friendList) return nullptr;

    for(auto& f : *friendList) {
        if(f.userId == userId) {
            return &f;
        }
    }
    return nullptr;
}

UserInfo DataCenter::removeFromApplyList(const QString& userId)
{
    if(!applyList) return UserInfo();

    for(int i = 0; i < applyList->size(); ++i) {
        if(applyList->at(i).userId == userId) {
            auto it = applyList->takeAt(i);
            return it;
        }
    }

    return UserInfo();
}


}


